<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <script>
    const PENDING = 'pending'
    const FULFILLED = 'fulfilled'
    const REJECTED = 'rejected'
    class MyPromise {
      constructor(executor) {
        const that = this
        this.status = PENDING
        this.onFulfilledCallbacks = [] // 完成回调
        this.onRejectedCallbacks = [] // 失败回调
        if (typeof executor !== 'function') throw TypeError('executor必须是函数')
        function resolve(value) {
          if(that.status === PENDING) {
            that.status = FULFILLED
            that.value = value
            that.onFulfilledCallbacks.forEach(cb => {
              cb(value)
            })
          }
        }
        function reject(reason) {
          if(that.status === PENDING) {
            that.status = REJECTED
            that.reason = reason
            that.onRejectedCallbacks.forEach(cb => {
              cb(reason)
            })
          }
        }
        // 如果执行器报错，就直接拒绝
        try {
          executor.call(that, resolve, reject) // 调用执行器
        } catch(e) {
          reject(e)
        }
      }
      then(onFulfilled, onRejected) {
        console.log('then执行了');
        
        const that = this
        // 不给对应的处理函数原样向下传递
        onFulfilled = isFunc(onFulfilled) ? onFulfilled : (value) => { value }
        onRejected = isFunc(onRejected) ? onRejected: (reason) => {
          throw reason
        }
        const promise = new MyPromise((resolve, reject) => {
          function fulfilledCallback(value) {
            queueMicrotask(() => {
              try {
                const result = onFulfilled(value)
                resolvePromise(promise, result, resolve, reject)
              } catch(e) {
                reject(e)
              }
            })
          }
          function rejectedCallback(reason) {
            queueMicrotask(() => {
              try {
                const result = onRejected(reason)
                resolvePromise(promise, result, resolve, reject)
              } catch(e) {
                reject(e)
              }
            })
          }
          switch(that.status) {
            case FULFILLED:
              fulfilledCallback(that.value)
              break
            case REJECTED:
              rejectedCallback(that.reason)
              break
            default: {
              that.onFulfilledCallbacks.push(fulfilledCallback)
              that.onRejectedCallbacks.push(rejectedCallback)
            }
          }
        })
        return promise
      }
      catch(onRejected) {
        return this.then(null, onRejected)
      }
    }
    function isFunc(fun) {
      return typeof fun === 'function'
    }
    function isObj(obj) {
      return obj instanceof Object
    }
    function resolvePromise(promise, data, resolve, reject) {
      if(data === promise) {
        return reject(new TypeError('禁止循环引用'))
      }
      let called = false // 多次调用resolve或者reject，以第一次为主，后面的直接忽略
      if((isObj(data) && data !== null) || isFunc(data)) {
        console.log('我会调用嘛');
        
        try {
          const then = data.then
          if(isFunc(then)) {
            then.call(data, value => {
              if(called) return
              called = true
              resolvePromise(promise, value, resolve, reject)
            }, reason => {
              if(called) return
              called = true
              reject(reason)
            })
          } else {
            resolve(data)
          }
        } catch(e) {
          if(called) {
            return
          }
          called = true
          reject(e)
        }
      } else {
        resolve(data)
      }
    }
    let p = new MyPromise((resolve, reject) => {
      setTimeout(() => {
        console.log('999');
        resolve('999')
      }, 2000)
    }).then((res) => {
      console.log('res', res);
    }).then(res => {
      console.log('res1', res);
     return this
    }).then(res => {
      console.log(res, '--------');
      throw new TypeError('====')
    }).catch(err => {
      console.log('err', err);
      
    })
  </script>
</body>

</html>